'''
Created on 2012-7-30

@author: Xindong
'''


from shortcuts import execute_insert, execute_sql, execute_select, log_info
import gexf


''' Later we should distinguish different global arcs
'''


def insert_global_arcs(arcs, level):
    '''
    @var arcs: list of (dep_node, arr_node, cost)
    Graph node levels:
        level 0: origin graph, in gtfs graph tables
        level 1: non-contracted non-hub nodes
        level 2: hubs nodes
        level 3: super hubs
    Graph arc levels:
        level k: highway arcs connecting k-level nodes
    '''
    for ((dep_dataset, dep_node), (arr_dataset, arr_node), cost) in arcs:
        inputs = [("dep_dataset", dep_dataset), ("dep_node", dep_node), 
                  ("arr_dataset", arr_dataset), ("arr_node", arr_node), 
                  ("cost", cost), ("level", level)]
        _arc_id = execute_insert("global_graph", inputs)
    execute_sql("commit")


def load_highway_graph(level):
    #TODO: not a good idea to load the entire highway graph
    graph = {}
    results = execute_select("""
        SELECT dep_dataset, dep_node, arr_dataset, arr_node, cost
        FROM global_graph WHERE level = %d
    """ % level)
    for item in results:
        dep_node = (int(item[0]), int(item[1]))
        arr_node = (int(item[2]), int(item[3]))
        cost = int(item[4])
        graph.setdefault(dep_node, dict()).setdefault(arr_node, cost)
        graph.setdefault(arr_node, dict())
    return graph


def load_walking_links(node_list = None):
    '''@return: a list of arcs [(dep, arr, cost), ...]
    '''
    #TODO: not a good idea to load the entire highway graph
    arcs = []
    results = execute_select("""
        SELECT dep_dataset, dep_node, arr_dataset, arr_node, cost
        FROM global_graph WHERE level = 0""")
    for item in results:
        dep_node = (int(item[0]), int(item[1]))
        arr_node = (int(item[2]), int(item[3]))
        cost = int(item[4])
        arcs.append((dep_node, arr_node, cost))
    return arcs


def load_coordinates(dataset_name = "gtfs"):
    '''@return: dict 
                - key:   (dataset_id, node_id)
                - value: (latitude, longitude)
    '''
    results = execute_select("""
        SELECT dataset_id, node_id, latitude, longitude FROM %s_graph_nodes
    """ % dataset_name)
    coordinates = {(int(dataset_id), int(node_id)): (int(latitude), int(longitude))
                   for dataset_id, node_id, latitude, longitude in results}
    return coordinates


def load_adjacency_matrix(dataset_name = "gtfs"):
    '''@return: dict
                - key:   (dataset_id, node_id)
                - value: a set of adjacent nodes
    '''
    adjacent = {node: set() for node in load_coordinates(dataset_name)}
    results = execute_select("""
        SELECT dataset_id, dep_node, arr_node
        FROM %s_graph_arcs
        GROUP BY dataset_id, dep_node, arr_node
    """ % dataset_name)
    for item in results:
        dep_node = (int(item[0]), int(item[1]))
        arr_node = (int(item[0]), int(item[2]))
        adjacent[dep_node].add(arr_node)
        adjacent[arr_node].add(dep_node)
    return adjacent


def load_local_graph(dataset_name = "gtfs", walking = True):
    '''@return: dict: an adjacent graph, with walking links by default
                - key:   (dataset_id, node_id)
                - value: a dict of adjacent nodes, and the minimal cost between them
    '''
    graph = {node: dict() for node in load_coordinates(dataset_name)}
    results = execute_select("""
        SELECT dataset_id, dep_node, arr_node, min(cost)
        FROM %s_graph_arcs
        GROUP BY dataset_id, dep_node, arr_node
    """ % dataset_name)
    for item in results:
        dep_node = (int(item[0]), int(item[1]))
        arr_node = (int(item[0]), int(item[2]))
        cost = int(item[3])
        graph[dep_node][arr_node] = cost
    if walking:
        walking_links = load_walking_links()
        for dep_node, arr_node, cost in walking_links:
            if dep_node in graph and arr_node in graph:
                graph[dep_node][arr_node] = cost
    return graph


def load_query_graph(route_set = None):
    '''@return: dict: a time dependent graph (sub graph introduced by certain routes)
        3 type of nodes:
            - the pysical station (we dont really put a graph node for it)
            - a node for each route
            - a entry/exit node for origin, destin and walking
        3 types of valued arcs:
            - route arc
            - transfers between route nodes on the same physical station
            - walking links from exit nodes to entry nodes
        non-valued arc:
            - arcs from route nodes to exit nodes (start walking or terminate)
            - arcs from entry nodes to route nodes (stop walking and waiting for bus)
    '''
    log_info("Load query graph, %s pre-fetched routes" % ["without","with"][route_set is not None])
    
    graph = {}
    arcnodes_out = {}
    arcnodes_in = {}
    
    def add_arc(dep, arr, value):
        graph.setdefault(dep, dict()).setdefault(arr, value)
        graph.setdefault(arr, dict())
    
    def append_arcnode_out(node, arcnode, route):
        arcnodes_out.setdefault(node, list()).append((arcnode, route))
        arcnodes_in.setdefault(node, list())
    
    def append_arcnode_in(node, arcnode, route):
        arcnodes_in.setdefault(node, list()).append((arcnode, route))
        arcnodes_out.setdefault(node, list())
    
    if route_set is not None:
        results = execute_select("""
            SELECT dataset_id, dep_node, arr_node, arc_id, route
            FROM gtfs_graph_arcs
            WHERE 0 %s
        """ % (" ".join(["OR dataset_id = %d AND route = %d" % route for route in route_set])))
    else:
        results = execute_select("SELECT dataset_id, dep_node, arr_node, arc_id, route FROM gtfs_graph_arcs")
    for item in results:
        dep_node = (int(item[0]), int(item[1]))
        arr_node = (int(item[0]), int(item[2]))
        
        dep_arcnode = (int(item[0]), int(item[1]), int(item[3]))
        arr_arcnode = (int(item[0]), int(item[2]), int(item[3]))
        
        arc = (int(item[0]), int(item[3]))
        route = (int(item[0]), int(item[4]))
        
        add_arc(dep_arcnode, arr_arcnode, value = {"route_arc": arc})
        
        add_arc(dep_node+("enter",), dep_arcnode, value = {})
        append_arcnode_out(dep_node, dep_arcnode, route)
        
        add_arc(arr_arcnode, arr_node+("exit",), value = {})
        append_arcnode_in(arr_node, arr_arcnode, route)
        
    """ for each node_arc on the same node, add transfer links """
    for node in arcnodes_in:
        for arcnode_in, route_in in arcnodes_in[node]:
            for arcnode_out, route_out in arcnodes_out[node]:
                transfer = [1, 0][route_in == route_out]
                add_arc(arcnode_in, arcnode_out, value={"transfer_arc": transfer})
    
    walking_links = load_walking_links()
    log_info("%d walking links loaded for query group" % len(walking_links))
    for dep_node, arr_node, cost in walking_links:
        walking_from = dep_node +("exit",)
        walking_to = arr_node +("enter",)
        if walking_from in graph and walking_to in graph:
            add_arc(walking_from, walking_to, value = {"walking_arc": cost})
    log_info("Graph size: %d" % len(graph))
    
    return graph


if __name__ == "__main__":
    graph = load_query_graph("test")
    node_coors = load_coordinates("test")
    gexf.output_time_dependent_graph(graph, node_coors, "full_graph")

